package com.cs.framework.jdbc.mybatis3.executor;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.jdbc.ConnectionLogger;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import org.springframework.jdbc.datasource.DataSourceUtils;

import com.cs.framework.jdbc.common.bo.PagingInfo;
import com.cs.framework.jdbc.common.util.DBConstant;
import com.cs.framework.jdbc.common.util.DBSqlUtils;
import com.cs.framework.jdbc.mybatis3.executor.statement.SelectStatementHandler;

/**
 * 仅提供查询的执行器
 * 
 * @author hedongzhou
 * 
 */
public class SelectExecutor implements Executor {

	private org.apache.commons.logging.Log logger = LogFactory.getLog(getClass());

	private Configuration configuration;

	private DataSource dataSource;

	private Connection connection;

	private String databaseProductName;

	public SelectExecutor(Configuration configuration, DataSource dataSource) {
		this.configuration = configuration;
		this.dataSource = dataSource;
		this.connection = DataSourceUtils.getConnection(dataSource);

		try {
			this.databaseProductName = connection.getMetaData().getDatabaseProductName();
		} catch (SQLException e) {
			this.databaseProductName = DBConstant.DEFAULT_DATABASE_PRODUCT;
		}
	}

	/**
	 * 查询
	 * 
	 * @param <T>
	 * @param sqlId
	 *            sql的ID
	 * @param parameter
	 *            参数
	 * @param pagingInfo
	 *            分页信息
	 * @param isCount
	 *            是否合计
	 * @param resultType
	 *            结果类型
	 * @return
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 */
	public <T> List<T> query(String sqlId, Object parameter, PagingInfo pagingInfo, Boolean isCount, Class<T> resultType) throws SQLException, ClassNotFoundException {
		MappedStatement ms = configuration.getMappedStatement(sqlId);

		parameter = DBSqlUtils.transformParameter(parameter);

		BoundSql boundSql = getBoundSql(ms, parameter, pagingInfo, isCount);

		Statement statement = null;
		try {
			SelectStatementHandler handler = new SelectStatementHandler(this, ms, parameter, boundSql);
			statement = prepareStatement(handler, ms.getStatementLog());
			return handler.<T> query(statement, resultType);
		} finally {
			if (statement != null) {
				try {
					statement.close();
				} catch (SQLException e) {
				}
			}
		}
	}

	/**
	 * 获取SQL信息
	 * 
	 * @param ms
	 *            映射声明
	 * @param parameter
	 *            参数
	 * @param pagingInfo
	 *            分页信息
	 * @param isCount
	 *            是否合计
	 * @return
	 */
	public BoundSql getBoundSql(MappedStatement ms, Object parameter, PagingInfo pagingInfo, Boolean isCount) {
		BoundSql boundSql = ms.getBoundSql(parameter);

		String sql = boundSql.getSql();
		if (isCount != null && isCount) {
			sql = DBSqlUtils.getCountSql(sql);
		} else if (pagingInfo != null) {
			setPagingInfo(pagingInfo);
			sql = DBSqlUtils.getPagingSql(sql, pagingInfo, databaseProductName);
		}

		return new BoundSql(configuration, sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
	}

	/**
	 * 获取SQL信息
	 * 
	 * @param sqlId
	 *            sql的ID
	 * @param parameter
	 *            参数
	 * @param pagingInfo
	 *            分页信息
	 * @param isCount
	 *            是否合计
	 * @return
	 */
	public BoundSql getBoundSql(String sqlId, Object parameter, PagingInfo pagingInfo, Boolean isCount) {
		MappedStatement ms = configuration.getMappedStatement(sqlId);
		return getBoundSql(ms, parameter, pagingInfo, isCount);
	}

	/**
	 * 设置分页信息
	 * 
	 * @param pagingInfo
	 */
	private void setPagingInfo(PagingInfo pagingInfo) {
		if (pagingInfo.getStartIndex() == null) {
			pagingInfo.setStartIndex(PagingInfo.DEFAULT_START_INDEX);
		}
		if (pagingInfo.getPageSize() == null) {
			pagingInfo.setPageSize(PagingInfo.DEFAULT_PAGE_SIZE);
		}
	}

	/**
	 * 获取声明对象
	 * 
	 * @param handler
	 * @param statementLog
	 * @return
	 * @throws SQLException
	 */
	private Statement prepareStatement(SelectStatementHandler handler, Log statementLog) throws SQLException {
		Statement statement;
		Connection connection = getConnection(statementLog);
		statement = handler.prepare(connection);
		handler.parameterize(statement);
		return statement;
	}

	/**
	 * 获取连接
	 * 
	 * @param statementLog
	 * @return
	 * @throws SQLException
	 */
	private Connection getConnection(Log statementLog) throws SQLException {
		if (statementLog.isDebugEnabled()) {
			return ConnectionLogger.newInstance(connection, statementLog);
		} else {
			return connection;
		}
	}

	public void close(boolean forceRollback) {
	}

	public void clearLocalCache() {
	}

	public void commit(boolean required) throws SQLException {
	}

	public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
		return null;
	}

	public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
	}

	public List<BatchResult> flushStatements() throws SQLException {
		return null;
	}

	public Transaction getTransaction() {
		return null;
	}

	public boolean isCached(MappedStatement ms, CacheKey key) {
		return false;
	}

	public boolean isClosed() {
		return false;
	}

	public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
		return null;
	}

	public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
		return null;
	}

	public void rollback(boolean required) throws SQLException {
	}

	public int update(MappedStatement ms, Object parameter) throws SQLException {
		return 0;
	}

}
